using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

namespace NodeCanvas.StateMachines
{
	[AddComponentMenu("")]
	public class FSM : Graph
	{
		private FSMState currentState;

		private FSMState lastState;

		private List<FSMAnyState> anyStates = new List<FSMAnyState>();

		private Dictionary<MonoBehaviour, MethodInfo> enterMethods = new Dictionary<MonoBehaviour, MethodInfo>();

		private Dictionary<MonoBehaviour, MethodInfo> stayMethods = new Dictionary<MonoBehaviour, MethodInfo>();

		private Dictionary<MonoBehaviour, MethodInfo> exitMethods = new Dictionary<MonoBehaviour, MethodInfo>();

		public string currentStateName
		{
			get
			{
				return (!(currentState != null)) ? null : currentState.nodeName;
			}
		}

		public string lastStateName
		{
			get
			{
				return (!(lastState != null)) ? null : lastState.nodeName;
			}
		}

		public override Type baseNodeType
		{
			get
			{
				return typeof(FSMState);
			}
		}

		protected override void OnGraphStarted()
		{
			GatherMethodInfo();
			anyStates.Clear();
			foreach (FSMState allNode in base.allNodes)
			{
				if (allNode.GetType() == typeof(FSMConcurrentState))
				{
					allNode.Execute(base.agent, base.blackboard);
				}
				if (allNode.GetType() == typeof(FSMAnyState))
				{
					anyStates.Add(allNode as FSMAnyState);
				}
			}
			EnterState((!(lastState == null)) ? lastState : (base.primeNode as FSMState));
		}

		protected override void OnGraphUpdate()
		{
			foreach (FSMAnyState anyState in anyStates)
			{
				anyState.UpdateAnyState();
			}
			currentState.OnUpdate();
			if (currentState.status == Status.Running)
			{
				CallbackStay(currentState);
			}
			if (currentState.status != Status.Running && currentState.outConnections.Count == 0)
			{
				StopGraph();
			}
		}

		protected override void OnGraphStoped()
		{
			lastState = null;
			currentState = null;
		}

		protected override void OnGraphPaused()
		{
			lastState = currentState;
			currentState = null;
		}

		public bool EnterState(FSMState newState)
		{
			if (!base.isRunning)
			{
				Debug.LogWarning("Tried to EnterState on an FSM that was not running", base.gameObject);
				return false;
			}
			if (newState == null)
			{
				Debug.LogWarning("Tried to Enter Null State");
				return false;
			}
			if (currentState != null)
			{
				currentState.Finish();
				currentState.ResetNode();
				CallbackExit(currentState);
				foreach (Connection inConnection in currentState.inConnections)
				{
					inConnection.connectionStatus = Status.Resting;
				}
			}
			lastState = currentState;
			currentState = newState;
			currentState.Execute(base.agent, base.blackboard);
			CallbackEnter(currentState);
			return true;
		}

		public FSMState TriggerState(string stateName)
		{
			FSMState stateWithName = GetStateWithName(stateName);
			if (stateWithName != null)
			{
				EnterState(stateWithName);
				return stateWithName;
			}
			Debug.LogWarning("No State with name '" + stateName + "' found on FSM '" + base.graphName + "'");
			return null;
		}

		public List<string> GetStateNames()
		{
			List<string> list = new List<string>();
			foreach (FSMState allNode in base.allNodes)
			{
				if (allNode.allowAsPrime)
				{
					list.Add(allNode.nodeName);
				}
			}
			return list;
		}

		public FSMState GetStateWithName(string name)
		{
			foreach (FSMState allNode in base.allNodes)
			{
				if (allNode.allowAsPrime && allNode.nodeName == name)
				{
					return allNode;
				}
			}
			return null;
		}

		private void CallbackEnter(IState state)
		{
			foreach (KeyValuePair<MonoBehaviour, MethodInfo> enterMethod in enterMethods)
			{
				enterMethod.Value.Invoke(enterMethod.Key, new object[1] { state });
			}
		}

		private void CallbackStay(IState state)
		{
			foreach (KeyValuePair<MonoBehaviour, MethodInfo> stayMethod in stayMethods)
			{
				stayMethod.Value.Invoke(stayMethod.Key, new object[1] { state });
			}
		}

		private void CallbackExit(IState state)
		{
			foreach (KeyValuePair<MonoBehaviour, MethodInfo> exitMethod in exitMethods)
			{
				exitMethod.Value.Invoke(exitMethod.Key, new object[1] { state });
			}
		}

		private void GatherMethodInfo()
		{
			enterMethods.Clear();
			stayMethods.Clear();
			exitMethods.Clear();
			MonoBehaviour[] components = base.agent.gameObject.GetComponents<MonoBehaviour>();
			foreach (MonoBehaviour monoBehaviour in components)
			{
				MethodInfo methodInfo = monoBehaviour.GetType().NCGetMethod("OnStateEnter");
				MethodInfo methodInfo2 = monoBehaviour.GetType().NCGetMethod("OnStateUpdate");
				MethodInfo methodInfo3 = monoBehaviour.GetType().NCGetMethod("OnStateExit");
				if (methodInfo != null)
				{
					enterMethods[monoBehaviour] = methodInfo;
				}
				if (methodInfo2 != null)
				{
					stayMethods[monoBehaviour] = methodInfo2;
				}
				if (methodInfo3 != null)
				{
					exitMethods[monoBehaviour] = methodInfo3;
				}
			}
		}
	}
}
